package org.example;

/**
 * 单例模式bug
 */
public class DCLBug {
    public static DCLBug INSTANCE;
    //添加volatile禁止JVM指令重排序
//    public static volatile DCLBug INSTANCE;
    public static DCLBug getInstance(){
        if(null == INSTANCE){
            try{
                Thread.sleep(1);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            /**
             * New的实际由以下命令组成， astore_1实际上就是已经创建好了，如果和invokespecial对调顺序实际就是没有初始化完。但这个在乱序执行里是可能发生的。
             * 所以如果乱序执行的话，会导致可能拿到的对象没有初始化变量。由于volatile只是控制了可见性和禁止重排，所以就不会拿到版初始化的对象了
             * 0 new
             * 3 dup
             * 4 invokespecial
             * 7 astore_1
             * 如果拿到半成品num默认是0不是8导致输出0
             * 0 new
             * 3 dup
             * 4 astore_1
             * 7 invokespecial
             * https://godbolt.org/ 可以看到
             */
            INSTANCE = new DCLBug();
        }
        return INSTANCE;
    }

    public static DCLBug getInstance2(){
        if(null == INSTANCE){
            synchronized (DCLBug.class){
                try{
                    Thread.sleep(1);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                INSTANCE = new DCLBug();
            }
        }
        return INSTANCE;
    }

    public static synchronized DCLBug getInstance3(){
        if(null == INSTANCE){
            try{
                Thread.sleep(1);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            INSTANCE = new DCLBug();
        }
        return INSTANCE;
    }

    //double check lock， 由于乱序执行会拿到一个没有完全初始化的对象。可以通过编译器禁止重排给对象加volatile标识，
    // 或者CPU内存屏障指令。
    public static DCLBug getInstance4(){
        //外面的检查可以提高效率，会将部分线程隔离出去。判断的时间花费小与抢锁。
        if(null == INSTANCE){
            synchronized (DCLBug.class){
                if(null == INSTANCE)
                {
                    try{
                        Thread.sleep(1);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    INSTANCE = new DCLBug();
                }
            }
        }
        return INSTANCE;
    }



    public static void main(String[] args){
//        for(int i = 0; i < 10; i++)
//        {
//            //打出多个不同的hash code
//            new Thread(()-> System.out.println(DCLBug.getInstance().hashCode())).start();
//        }

//        for(int i = 0; i < 10; i++)
//        {
//            //打出多个不同的hash code
//            new Thread(()-> System.out.println(DCLBug.getInstance2().hashCode())).start();
//        }

//        for(int i = 0; i < 10; i++)
//        {
//            //不会打出多个不同的hash code
//            new Thread(()-> System.out.println(DCLBug.getInstance3().hashCode())).start();
//        }

        for(int i = 0; i < 10; i++)
        {
            //不会打出多个不同的hash code
            new Thread(()-> System.out.println(DCLBug.getInstance4().hashCode())).start();
        }
    }
}
